package com.yeskery.nut.util.logging;

import com.yeskery.nut.core.BasicNutConfigure;
import com.yeskery.nut.core.Environment;
import com.yeskery.nut.core.NutException;
import com.yeskery.nut.util.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.LogManager;

/**
 * Nut日志初始化器
 * @author Yeskery
 * 2023/7/19
 */
public class NutLoggingInitializer {

    /** 控制台处理器 */
    private static final String CONSOLE_HANDLER = "com.yeskery.nut.util.logging.NutLoggingConsoleHandler";

    /** 文件处理器 */
    private static final String FILE_HANDLER = "java.util.logging.FileHandler";

    /** 默认处理器 */
    private static final String DEFAULT_HANDLERS = CONSOLE_HANDLER + "," + FILE_HANDLER;

    /** 默认格式化器 */
    private static final String DEFAULT_FORMATTER = "com.yeskery.nut.util.logging.NutLoggingFormatter";

    /** level前缀 */
    private static final String LEVEL_PREFIX = "logging.level.";

    /** level后缀 */
    private static final String LEVEL_SUFFIX = ".level";

    /** jar后缀 */
    private static final String JAR_SUFFIX = ".jar";

    /** jar包后缀 */
    private static final String JAR_FILE_SUFFIX = ".jar!";

    /** jar包依赖后缀 */
    private static final String JAR_FILE_LIB_SUFFIX = JAR_FILE_SUFFIX + "/lib";

    /** 根路径 */
    private static final String ROOT_PATH = "/";

    /** 默认日志存储路径  */
    private static final String DEFAULT_LOG_DIR = "logs";

    /** 日志文件名 */
    private static final String DEFAULT_LOG_FILE_NAME = "all";

    /** 日志配置内容 */
    private static volatile String logConfigContent;

    /**
     * 初始化日志配置
     * @param basicNutConfigure nut配置对象
     */
    public static void init(BasicNutConfigure basicNutConfigure) {
        init(basicNutConfigure, null);
    }

    /**
     * 初始化日志配置
     * @param basicNutConfigure nut配置对象
     * @param environment 环境对象
     */
    public static void init(BasicNutConfigure basicNutConfigure, Environment environment) {
        try {
            if (logConfigContent == null) {
                logConfigContent = getLogConfigContent(basicNutConfigure, environment);
                LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(logConfigContent.getBytes(StandardCharsets.UTF_8)));
            } else {
                String configContent = getCurrentLogConfigContent(basicNutConfigure, environment);
                if (!configContent.equals(logConfigContent)) {
                    LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(configContent.getBytes(StandardCharsets.UTF_8)));
                }
            }
        } catch (IOException e) {
            System.out.println("Logging Config Config Fail." + e.getMessage());
        }
    }

    /**
     * 重置日志配置内容
     */
    public synchronized static void resetLogConfigContent() {
        logConfigContent = null;
    }

    /**
     * 获取日志配置内容
     * @param basicNutConfigure nut配置对象
     * @param environment 环境对象
     * @return 日志配置内容
     */
    private static String getLogConfigContent(BasicNutConfigure basicNutConfigure, Environment environment) {
        if (logConfigContent == null) {
            synchronized (NutLoggingInitializer.class) {
                if (logConfigContent == null) {
                    logConfigContent = getCurrentLogConfigContent(basicNutConfigure, environment);
                }
            }
        }
        return logConfigContent;
    }

    /**
     * 获取当前日志配置内容
     * @param basicNutConfigure nut配置对象
     * @param environment 环境对象
     * @return 当前日志配置内容
     */
    private static String getCurrentLogConfigContent(BasicNutConfigure basicNutConfigure, Environment environment) {
        StringBuilder logStringBuilder = new StringBuilder();
        String handlers = getHandlers(basicNutConfigure);
        Level level = getLevel(basicNutConfigure);
        String logFormatter = getLogFormatter(basicNutConfigure);
        logStringBuilder.append("handlers=").append(handlers).append("\r\n");
        logStringBuilder.append(".level=").append(level).append("\r\n");
        String[] handlersSplits = handlers.split(",");
        for (String handlersSplit : handlersSplits) {
            logStringBuilder.append(handlersSplit).append(".level=").append(Level.FINEST).append("\r\n");
            logStringBuilder.append(handlersSplit).append(".formatter=").append(logFormatter).append("\r\n");
        }
        if (handlers.contains(FILE_HANDLER)) {
            logStringBuilder.append(FILE_HANDLER).append(".encoding=").append(StandardCharsets.UTF_8.name()).append("\r\n");
            logStringBuilder.append(FILE_HANDLER).append(".limit=").append(20971520).append("\r\n");
            logStringBuilder.append(FILE_HANDLER).append(".count=").append(10).append("\r\n");
            logStringBuilder.append(FILE_HANDLER).append(".pattern=").append(getLogDirPath(basicNutConfigure).replace("\\", "/"))
                    .append("/").append(DEFAULT_LOG_FILE_NAME).append(".%g.log").append("\r\n");
        }
        if (environment != null) {
            for (Map.Entry<Object, Object> entry : environment.getEnvProperties().entrySet()) {
                String key = (String) entry.getKey();
                if (!StringUtils.isEmpty(key) && key.startsWith(LEVEL_PREFIX) && key.length() > LEVEL_PREFIX.length()) {
                    String value = (String) entry.getValue();
                    if (!StringUtils.isEmpty(value)) {
                        try {
                            if (Level.parse(value) != null) {
                                logStringBuilder.append(key.substring(LEVEL_PREFIX.length())).append(LEVEL_SUFFIX).append("=").append(value).append("\r\n");
                            }
                        } catch (IllegalArgumentException e) {
                            System.out.println("Wrong Package Level Config [" + key + "=" + value + "] Current Package Level Is Set To [INFO]");
                        }
                    }
                }
            }
        }
        return logStringBuilder.toString();
    }

    /**
     * 获取日志级别
     * @param basicNutConfigure nut配置对象
     * @return 日志级别
     */
    private static Level getLevel(BasicNutConfigure basicNutConfigure) {
        if (isDebugModel()) {
            return Level.FINEST;
        }
        if (StringUtils.isEmpty(basicNutConfigure.getLogLevel())) {
            return Level.INFO;
        } else {
            try {
                return Level.parse(basicNutConfigure.getLogLevel());
            } catch (IllegalArgumentException e) {
                System.out.println("Wrong Level Config [" + basicNutConfigure.getLogLevel() + "] Current Level Is Set To [INFO]");
                return Level.INFO;
            }
        }
    }

    /**
     * 获取处理器
     * @param basicNutConfigure nut配置对象
     * @return 处理器
     */
    private static String getHandlers(BasicNutConfigure basicNutConfigure) {
        return StringUtils.isEmpty(basicNutConfigure.getLogHandlers()) ? isDebugModel() ? CONSOLE_HANDLER : DEFAULT_HANDLERS : basicNutConfigure.getLogHandlers();
    }

    /**
     * 获取日志格式化器
     * @param basicNutConfigure nut配置对象
     * @return 日志格式化器
     */
    private static String getLogFormatter(BasicNutConfigure basicNutConfigure) {
        return StringUtils.isEmpty(basicNutConfigure.getLogFormatter()) ? DEFAULT_FORMATTER : basicNutConfigure.getLogFormatter();
    }

    /**
     * 获取日志存储目录
     * @param basicNutConfigure nut配置对象
     * @return 日志存储目录
     */
    private static String getLogDir(BasicNutConfigure basicNutConfigure) {
        return StringUtils.isEmpty(basicNutConfigure.getLogDir()) ? DEFAULT_LOG_DIR : basicNutConfigure.getLogDir();
    }

    /**
     * 获取日志存储目录路径
     * @param basicNutConfigure nut配置对象
     * @return 日志存储目录路径
     */
    private static String getLogDirPath(BasicNutConfigure basicNutConfigure) {
        String logDirPath = getLogDir(basicNutConfigure);
        if (DEFAULT_LOG_DIR.equals(logDirPath)) {
            try {
                String path = NutLoggingInitializer.class.getProtectionDomain().getCodeSource().getLocation().getPath();
                File logDirFile;
                if (StringUtils.isEmpty(path)) {
                    URL rootUrl = NutLoggingInitializer.class.getResource(ROOT_PATH);
                    if (rootUrl == null) {
                        throw new NutException("Root Resource Directory Can Not Found.");
                    }
                    logDirFile = new File(new URI(rootUrl.toURI() + DEFAULT_LOG_DIR));
                } else {
                    String logDirFilePath = path;
                    while (logDirFilePath.endsWith(JAR_SUFFIX) || logDirFilePath.endsWith(JAR_FILE_SUFFIX)
                            || logDirFilePath.endsWith(JAR_FILE_LIB_SUFFIX)) {
                        logDirFilePath = logDirFilePath.substring(0, path.lastIndexOf(ROOT_PATH));
                    }
                    if (!logDirFilePath.endsWith(ROOT_PATH)) {
                        logDirFilePath += ROOT_PATH;
                    }
                    logDirFilePath += DEFAULT_LOG_DIR;
                    logDirFile = new File(logDirFilePath);
                }
                if (!logDirFile.exists() && !logDirFile.mkdirs()) {
                    throw new NutException("Log File Directory Can Not Be Created.");
                }
                logDirPath = logDirFile.getAbsolutePath();
            } catch (URISyntaxException e) {
                throw new NutException("Root Resource Directory URI Parse Fail.", e);
            }
        }
        return logDirPath;
    }

    /**
     * 当前java进程是否是debug方式启动
     * @return 当前java进程是否是debug方式启动
     */
    private static boolean isDebugModel() {
        String args = ManagementFactory.getRuntimeMXBean().
                getInputArguments().toString();
        return args.indexOf("-agentlib:jdwp") > 0 || args.indexOf("-Xdebug") > 0;
    }
}
